package com.jwsoft.manager.core.integration.common.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.bifang.engine.assistant.core.annotation.ApiService;
import com.bifang.engine.assistant.core.annotation.OpApi;
import com.bifang.engine.assistant.core.annotation.SaveOplog;
import com.bifang.engine.assistant.core.domain.AppResponse;
import com.bifang.engine.assistant.core.enums.AuTypeEnum;
import com.bifang.engine.assistant.core.enums.BoolEnum;
import com.bifang.engine.assistant.core.enums.FuncTypeEnum;
import com.bifang.engine.assistant.core.exception.AppException;
import com.bifang.engine.assistant.core.utils.SessionUtil;
import com.bifang.engine.data.core.util.PagerUtil;
import com.bifang.module.base.common.vo.common.CommonVO;
import com.bifang.module.base.core.helper.DictHelper;
import com.bifang.module.base.core.integration.CommonIntegration;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jwsoft.manager.common.enums.PdfSignRequestDictEnum;
import com.jwsoft.manager.common.vo.common.pdfSignRequest.PdfSignRequestKeyVO;
import com.jwsoft.manager.common.vo.common.pdfSignRequest.PdfSignRequestQueryVO;
import com.jwsoft.manager.common.vo.common.pdfSignRequest.PdfSignRequestVO;
import com.jwsoft.manager.core.dao.model.PdfSignRequest;
import com.jwsoft.manager.core.dao.service.PdfSignRequestService;
import com.jwsoft.manager.core.integration.common.PdfSignRequestIntegration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * PDF签章请求模板表业务实现类
 *
 * @author zhang
 * @since 2025-02-06
 */
@ApiService(funcCode = "pdfSignRequest", title = "PDF签章请求模板表")
@Slf4j
public class PdfSignRequestIntegrationImpl implements PdfSignRequestIntegration {

    @Autowired
    public PdfSignRequestService pdfSignRequestService;

    @Autowired
    public DictHelper dictHelper;

    @Value("${spring.servlet.multipart.location}")
    private String tempPath;

    @Autowired
    private CommonIntegration commonIntegration;

    // private final String URL = "http://10.185.232.79:9011/irs/v1/rest/sign/signPdf";
    private final String ACCESS_KEY = "d6f1fa5e4ec34d08a36f501d8a41dfb1";
    private final String SECRET_KEY = "b841093ea09e4661a8b10d7775476b46";

    @Override
    @OpApi(funcCode = "pdfSignRequest0001", title = "PDF签章请求模板表分页查询", funcType = FuncTypeEnum.query,checkPrivilege = BoolEnum.FALSE)
    public PageInfo<PdfSignRequestVO> getList(PdfSignRequestQueryVO vo) {
        // 开启分页
        if (vo.getPageNum() == null || vo.getPageSize() == null) {
            //如果不传入分页参数，默认最大取1000条
            vo.setPageNum(1);
            vo.setPageSize(1000);
        }
        QueryWrapper<PdfSignRequest> pdfSignRequestQueryWrapper = new QueryWrapper<>();
        pdfSignRequestQueryWrapper.lambda()
                .eq(!ObjectUtils.isEmpty(vo.getId()), PdfSignRequest::getId, vo.getId())
                .eq(!ObjectUtils.isEmpty(vo.getTemplateType()), PdfSignRequest::getTemplateType, vo.getTemplateType())
                .eq(!ObjectUtils.isEmpty(vo.getSignType()), PdfSignRequest::getSignType, vo.getSignType());
        PageHelper.startPage(vo.getPageNum(),vo.getPageSize());
        List<PdfSignRequest> list = pdfSignRequestService.list(pdfSignRequestQueryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            // 判断是否为空，为空则返回空参数
            return PagerUtil.createEmptyPagerInfo(vo.getPageNum(), vo.getPageSize());
        }
        // 不为空则获取分页结果
        PageInfo<PdfSignRequest> pageInfo = new PageInfo<>(list);
        // 返回参数需要通过common层的vo实体类，用该方法可以批量转换
        List<PdfSignRequestVO> resultList = Convert.toList(PdfSignRequestVO.class, pageInfo.getList());
        // 转义数据字典值
        dictHelper.valueToName(resultList, Arrays.asList(PdfSignRequestDictEnum.values()));
        return PagerUtil.parsePagerVo(resultList, pageInfo);
    }

    @Override
    @OpApi(funcCode = "pdfSignRequest0002", title = "PDF签章请求模板表根据主键查询详情", funcType = FuncTypeEnum.query,checkPrivilege = BoolEnum.FALSE)
    public PdfSignRequestVO getById(PdfSignRequestKeyVO vo) {
        PdfSignRequest entity = pdfSignRequestService.getById(vo.getId());
        if (ObjectUtils.isEmpty(entity)) {
            throw new AppException("查询不到详细信息!");
        }
        PdfSignRequestVO result = Convert.convert(PdfSignRequestVO.class, entity);
        return result;
    }
    /** 保存前统一校验*/
    @Override
    @OpApi(funcCode = "pdfSignRequest0003", title = "PDF签章请求模板表保存前校验", funcType = FuncTypeEnum.other ,checkPrivilege = BoolEnum.FALSE)
    public void checkSave(PdfSignRequestVO vo) {
        if (ObjectUtils.isEmpty(vo.getTemplateType())){
            throw new AppException("模板类型不能为空");
        }
        if (ObjectUtils.isEmpty(vo.getSignType())){
            throw new AppException("签署类型不能为空");
        }

        QueryWrapper<PdfSignRequest> pdfSignRequestQueryWrapper = new QueryWrapper<>();
        pdfSignRequestQueryWrapper.lambda()
                .eq(!ObjectUtils.isEmpty(vo.getTemplateType()), PdfSignRequest::getTemplateType, vo.getTemplateType())
                .ne(!ObjectUtils.isEmpty(vo.getId()), PdfSignRequest::getId, vo.getId());
        if (pdfSignRequestService.count(pdfSignRequestQueryWrapper) > 0){
            throw new AppException("该模板类型已存在");
        }
        //主键为空为新增校验

        //主键不为空为修改校验
    }

    @Override
    @OpApi(funcCode = "pdfSignRequest0004", title = "PDF签章请求模板表新增", funcType = FuncTypeEnum.insert)
    @SaveOplog(
            operationType = "pdfSignRequest0004",
            operationName = "PDF签章请求模板表新增",
            dataType = "id",
            auType = AuTypeEnum.OTHER)
    @Transactional
    public PdfSignRequestVO add(PdfSignRequestVO vo) {
        //保存前校验
        checkSave(vo);
        //保存数据
        PdfSignRequest entity = Convert.convert(PdfSignRequest.class, vo);
        pdfSignRequestService.save(entity);
        //将主键设置回vo使操作日志可以取到
        vo.setId(entity.getId());
        return vo;
    }

    @Override
    @OpApi(funcCode = "pdfSignRequest0005", title = "PDF签章请求模板表修改", funcType = FuncTypeEnum.update)
    @SaveOplog(
            operationType = "pdfSignRequest0005",
            operationName = "PDF签章请求模板表修改",
            dataType = "id",
            auType = AuTypeEnum.OTHER)
    @Transactional
    public PdfSignRequestVO edit(PdfSignRequestVO vo) {
        if (ObjectUtils.isEmpty(vo.getId())) {
            throw new AppException("传入需要修改的主键不能为空!");
        }
        PdfSignRequest oldEntity = pdfSignRequestService.getById(vo.getId());
        if (ObjectUtils.isEmpty(oldEntity)) {
            throw new AppException("查询不到需要修改的信息!");
        }
        //保存前校验
        checkSave(vo);
        //保存数据
        PdfSignRequest entity = Convert.convert(PdfSignRequest.class, vo);
        pdfSignRequestService.updateById(entity);
        return vo;
    }

    @Override
    @OpApi(funcCode = "pdfSignRequest0006", title = "PDF签章请求模板表根据主键删除", funcType = FuncTypeEnum.delete)
    @SaveOplog(
            operationType = "pdfSignRequest0006",
            operationName = "PDF签章请求模板表删除",
            dataType = "id",
            auType = AuTypeEnum.OTHER)
    @Transactional
    public void del(PdfSignRequestKeyVO vo) {
        if (ObjectUtils.isEmpty(vo.getId())) {
            throw new AppException("传入需要删除的数据主键不能为空!");
        }
        PdfSignRequest entity = pdfSignRequestService.getById(vo.getId());
        if (ObjectUtils.isEmpty(entity)) {
            throw new AppException("未找到需要删除的信息!");
        }
        pdfSignRequestService.removeById(vo.getId());
    }

    @Override
    @OpApi(funcCode = "pdfSignRequest0007", title = "PDF签章请求公共接口", funcType = FuncTypeEnum.other ,checkPrivilege = BoolEnum.FALSE)
    public void getSignRequest(PdfSignRequestVO vo) {
        if (ObjectUtils.isEmpty(vo.getShowId())) {
            throw new AppException("文件showId不能为空");
        }
        if (ObjectUtils.isEmpty(vo.getTemplateType())) {
            throw new AppException("模板类型不能为空");
        }
        if (StringUtils.isEmpty(vo.getFileName())) {
            throw new AppException("文件名不能为空!");
        }

        String showId = vo.getShowId();
        String templateType = vo.getTemplateType();
        String fileName = vo.getFileName();

        // 去除非法字符和多余空格，保留英文、数字、下划线、中文、点（用于扩展名）
        fileName = fileName.replaceAll("[^a-zA-Z0-9_\\u4e00-\\u9fa5.]", "").replaceAll("\\s+", "");

        // 判断并获取文件扩展名，没有扩展名则默认加 .pdf
        String fileExtension = "";
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex != -1 && lastDotIndex < fileName.length() - 1) {
            fileExtension = fileName.substring(lastDotIndex + 1);
            fileName = fileName.substring(0, lastDotIndex); // 去除扩展名部分
        } else {
            fileExtension = "pdf"; // 默认扩展名
        }

        CommonVO commonVO4= new CommonVO();
        commonVO4.setParamKey("show_post_url");
        commonVO4.setAreaCode(SessionUtil.getAreaCode());
        String postUrl = commonIntegration.getValueByKeyAndArea(commonVO4);
        if (org.apache.commons.lang3.StringUtils.isBlank(postUrl)) {
            throw new AppException("请先配置show_post_url");
        }

        // 根据showId查询文件
        // String postUrl = "https://dbfile.dyedu.net/dyeduFile/api/common/file/show/20230001";
        String fileUrl = postUrl + "/" + showId;

        // 随机生成一个UUID，确保文件名唯一
        UUID uuid = UUID.randomUUID();
        String saveFileName = fileName + "_" + uuid + "." + fileExtension;

        File file = FileUtil.file(tempPath + "/" + saveFileName);

        try {
            // 下载文件
            HttpUtil.downloadFile(fileUrl, file);

            // 执行签章请求
            String returnShowId = this.executeSignRequest(file, templateType, fileName);
            log.info("签章成功，返回的showId: " + returnShowId);

        } catch (Exception e) {
            log.error("文件处理失败: ", e);
            throw new AppException("文件处理失败: " + e.getMessage(), e);

        } finally {
            // 确保文件被删除
            if (file.exists()) {
                boolean deleted = file.delete();
                if (deleted) {
                    log.info("临时文件已成功删除: " + file.getAbsolutePath());
                } else {
                    log.warn("无法删除临时文件: " + file.getAbsolutePath());
                }
            }
        }
    }


    /**
     * PDF签章请求公共方法
     * @param file 需要签章的文件
     * @param templateType 模板类型
     * @param fileName 文件名
     * @return
     */
    public String executeSignRequest(File file, String templateType, String fileName) {
        if (com.baomidou.mybatisplus.core.toolkit.ObjectUtils.isEmpty(file)) {
            throw new AppException("需要签章的文件不能为空!");
        }
        if (StringUtils.isEmpty(templateType)) {
            throw new AppException("模板类型不能为空!");
        }
        if (StringUtils.isEmpty(fileName)) {
            throw new AppException("文件名不能为空!");
        }

        // 查询模板信息
        PdfSignRequest pdfSignRequest = getPdfSignRequest(templateType);
        if (pdfSignRequest == null) {
            throw new AppException("未找到对应模板类型的签章请求信息!");
        }

        try {
            // 转换文件为 Base64
            String fileBase64 = convertFileToBase64(file);

            // 构建请求体
            Map<String, Object> requestMap = buildRequestMap(pdfSignRequest, fileBase64, fileName);

            String query = JSON.toJSONString(requestMap);

            CommonVO commonVO3 = new CommonVO();
            commonVO3.setParamKey("SECRET_KEY");
            commonVO3.setAreaCode(SessionUtil.getAreaCode());
            String SECRET_KEY = commonIntegration.getValueByKeyAndArea(commonVO3);
            if (org.apache.commons.lang3.StringUtils.isBlank(SECRET_KEY)) {
                throw new AppException("请先配置SECRET_KEY");
            }

            // 生成签名
            String signature = getSignature(query, SECRET_KEY);

            // 发送 HTTP 请求
            String response = sendHttpRequest(query, signature);

            return processResponse(response, fileName);
        } catch (Exception e) {
            log.error("签章请求失败: {}", e.getMessage(), e);
            throw new RuntimeException("签章请求失败: " + e.getMessage(), e);
        }
    }

    /**
     * 根据模板类型查询模板信息
     *
     * @param templateType
     * @return
     */
    private PdfSignRequest getPdfSignRequest(String templateType) {
        QueryWrapper<PdfSignRequest> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(PdfSignRequest::getTemplateType, templateType);
        List<PdfSignRequest> list = pdfSignRequestService.list(queryWrapper);

        if (CollectionUtils.isEmpty(list)) {
            throw new AppException("未找到模板信息!");
        }
        if (list.size() > 1) {
            throw new AppException("存在多条模板信息，请联系管理员处理!");
        }
        return list.get(0);
    }

    /**
     * 将文件转换为 Base64
     *
     * @param file
     * @return
     * @throws IOException
     */
    public String convertFileToBase64(File file) throws IOException {
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            byte[] fileBytes = new byte[(int) file.length()];
            fileInputStream.read(fileBytes);
            return Base64.getEncoder().encodeToString(fileBytes);
        }
    }

    /**
     * @param pdfSignRequest
     * @param fileBase64
     * @param fileName
     * @return
     */
    private Map<String, Object> buildRequestMap(PdfSignRequest pdfSignRequest, String fileBase64, String fileName) {
        Map<String, Object> requestMap = new HashMap<>();
        requestMap.put("fileBase64", fileBase64);
        requestMap.put("posPage", pdfSignRequest.getPosPage());
        requestMap.put("posX", pdfSignRequest.getPosX());
        requestMap.put("posY", pdfSignRequest.getPosY());
        requestMap.put("key", pdfSignRequest.getKeyName());
        requestMap.put("sealSn", pdfSignRequest.getSealSn());
        requestMap.put("fileName", fileName);
        requestMap.put("signType", Integer.valueOf(pdfSignRequest.getSignType()));
        requestMap.put("pwdB64", pdfSignRequest.getPwdB64());
        requestMap.put("keyIndex", pdfSignRequest.getKeyIndex());
        requestMap.put("addSignTime", pdfSignRequest.getAddSignTime());
        return requestMap;
    }

    /**
     * 生成签名
     *
     * @param param
     * @param secret
     * @return
     */
    public String getSignature(String param, String secret) {
        try {
            Mac hmacSha256 = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            hmacSha256.init(secretKey);
            byte[] hash = hmacSha256.doFinal(param.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                hexString.append(String.format("%02x", b));
            }
            return hexString.toString();
        } catch (Exception e) {
            throw new RuntimeException("生成签名失败: " + e.getMessage(), e);
        }
    }

    /**
     * 发送 HTTP 请求
     *
     * @param query
     * @param signature
     * @return
     */
    private String sendHttpRequest(String query, String signature) {
        CommonVO commonVO = new CommonVO();
        commonVO.setParamKey("IRS_URL");
        commonVO.setAreaCode(SessionUtil.getAreaCode());
        String URl = commonIntegration.getValueByKeyAndArea(commonVO);
        if (org.apache.commons.lang3.StringUtils.isBlank(URl)) {
            throw new AppException("请先配置请求接口地址");
        }

        CommonVO commonVO2 = new CommonVO();
        commonVO2.setParamKey("ACCESS_KEY");
        commonVO2.setAreaCode(SessionUtil.getAreaCode());
        String ACCESS_KEY = commonIntegration.getValueByKeyAndArea(commonVO2);
        if (org.apache.commons.lang3.StringUtils.isBlank(ACCESS_KEY)) {
            throw new AppException("请先配置ACCESS_KEY");
        }


        HttpResponse response = HttpRequest.post(URl)
                .header("AccessKey", ACCESS_KEY)
                .header("Signature", signature)
                .header("Content-Type", "application/json")
                .body(query)
                .timeout(5000)
                .execute();

        return response.body();
    }


    /**
     * 处理响应
     *
     * @param response
     * @param fileName
     */
    private String processResponse(String response, String fileName) {
        if (org.apache.commons.lang3.StringUtils.isBlank(response)) {
            throw new AppException("获取信息失败，未返回信息");
        }
        if (org.apache.commons.lang3.StringUtils.isBlank(response.trim())) {
            throw new AppException("获取信息失败，未返回信息");
        }
        Map<String, Object> responseMap;
        try {
            responseMap = JSON.parseObject(response, Map.class);
        } catch (Exception e) {
            log.error("转换信息失败：{}", response);
            throw new AppException("转换信息失败：" + response);
        }

        if (Boolean.TRUE.equals(responseMap.get("success")) && Integer.valueOf(0).equals(responseMap.get("code"))) {
            Map<String, Object> data = (Map<String, Object>) responseMap.get("data");
            String signFileB64 = (String) data.get("signFileB64");

            if (StringUtils.isNotEmpty(signFileB64)) {
                log.info("签署成功，盖好章的PDF文件Base64为: {}", signFileB64);

                try {
                    return uploadDecodedFile(signFileB64, fileName);
                } catch (Exception e) {
                    log.error("文件上传失败！");
                    throw new AppException("文件上传失败！");
                }
            } else {
                log.error("签署成功，但返回的签署文件内容为空！");
                throw new AppException("签署成功，但返回的签署文件内容为空！");
            }
        } else {
            log.error("签署失败，错误码: {}，错误信息: {}", responseMap.get("code"), responseMap.get("message"));
            throw new AppException("签署失败，错误码: " + responseMap.get("code") + "，错误信息: " + responseMap.get("message"));
        }
    }

    /**
     * @param signFileB64
     * @param fileName
     * @return
     */
    public String uploadDecodedFile(String signFileB64, String fileName) {
        // 去除非法字符和空格
        fileName = fileName.replaceAll("[^a-zA-Z0-9_\\u4e00-\\u9fa5]", "").replaceAll("\\s+", "");

        // 后缀
        String fileExtension = "";
        if (fileName.contains(".")) {
            String[] parts = fileName.split("\\.");
            fileExtension = parts[parts.length - 1];
        }

        File tempFile = null;
        try {

            CommonVO commonVO5= new CommonVO();
            commonVO5.setParamKey("upload_post_url");
            commonVO5.setAreaCode(SessionUtil.getAreaCode());
            String postUrl = commonIntegration.getValueByKeyAndArea(commonVO5);
            if (org.apache.commons.lang3.StringUtils.isBlank(postUrl)) {
                throw new AppException("请先配置show_post_url");
            }
            // String postUrl = "https://dbfile.dyedu.net/dyeduFile/api/common/file/upload/20230001";

            // 解码 Base64 内容
            byte[] decodedBytes = Base64.getDecoder().decode(signFileB64);

            // 将字节数组写入临时文件
            tempFile = File.createTempFile("decodedFile_", "." + fileExtension);
            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                fos.write(decodedBytes);
            }

            // 上传文件
            String timestamp = String.valueOf(System.currentTimeMillis());

            CommonVO commonVO6= new CommonVO();
            commonVO6.setParamKey("app_secret");
            commonVO6.setAreaCode(SessionUtil.getAreaCode());
            String appSecret = commonIntegration.getValueByKeyAndArea(commonVO6);
            if (org.apache.commons.lang3.StringUtils.isBlank(appSecret)) {
                throw new AppException("请先配置app_secret");
            }

            CommonVO commonVO7= new CommonVO();
            commonVO7.setParamKey("file_appId");
            commonVO7.setAreaCode(SessionUtil.getAreaCode());
            String appId = commonIntegration.getValueByKeyAndArea(commonVO7);
            if (org.apache.commons.lang3.StringUtils.isBlank(appId)) {
                throw new AppException("请先配置file_appId");
            }

            // String appId = "20230001";
            String appKey = "04660e7da5f32f367a053f3636ff4ee3c17b124e881a0eaa47ad82b98ec223150759f2cdee1ebecf7215cf310d6df31d49be0d4bd8e444339d0a49cfa9f6ddf003";
            // String appSecret = "00d5e6c4da14ace16498551f1c922c37c7298561cfdea02623d1edfef49dd510a3";
            String signature = SmUtil.sm3("appId=" + appId + "&secret=" + appSecret + "&timestamp=" + timestamp).toUpperCase();
            if (org.apache.commons.lang3.StringUtils.isBlank(signature)) {
                throw new AppException("签名不能为空");
            }

            // 文件上传请求参数
            HashMap<String, Object> fileInfo = new HashMap<>();
            fileInfo.put("fileClassCode", "50");
            fileInfo.put("fileName", fileName);
            fileInfo.put("fileSuffix", fileExtension);

            // 上传文件请求
            String body = HttpUtil.createPost(postUrl)
                    .header("appId", appId)
                    .header("signature", signature)
                    .header("timestamp", timestamp)
                    .form("file", tempFile) // 上传解码后的临时文件
                    .form("fileInfo", JSON.toJSONString(fileInfo))
                    .execute()
                    .body();

            // 解析响应
            AppResponse appResponse = JSON.parseObject(body, AppResponse.class);
            if (appResponse.getCode() == 0) {
                String showId = parseShowId(appResponse.getResult());
                return showId;
            } else {
                throw new AppException("文件处理失败: " + appResponse.getMessage());
            }
        } catch (IOException e) {
            throw new AppException("文件处理过程中发生错误", e);
        } finally {
            // 删除临时文件
            if (tempFile != null && tempFile.exists() && !tempFile.delete()) {
                log.error("警告：无法删除临时文件 " + tempFile.getAbsolutePath());
            }
        }
    }


    // 解析 showId 的方法
    private String parseShowId(Object result) {
        if (result == null) {
            return null;
        }
        // 将 result 转换为 JSON 对象并提取 showId
        JSONObject jsonObject;
        if (result instanceof String) {
            jsonObject = JSON.parseObject((String) result);
        } else {
            jsonObject = JSON.parseObject(JSON.toJSONString(result));
        }

        return jsonObject.getJSONObject("file").getString("showId");
    }

}
